#include "scripting/js-bindings/auto/jsb_cocos2dx_myjsb_auto.hpp"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)

#include "scripting/js-bindings/manual/jsb_conversions.hpp"
#include "scripting/js-bindings/manual/jsb_global.h"
#include "myjsb/Goddess.h"
#include "myjsb/Gift.h"

se::Object* __jsb_cocos2d_myjsb_Goddess_proto = nullptr;
se::Class* __jsb_cocos2d_myjsb_Goddess_class = nullptr;

static bool js_cocos2d_myjsb_Goddess_getInstance(se::State &s) {
    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 0) {
        cocos2d::myjsb::Goddess* result = cocos2d::myjsb::Goddess::getInstance(); // C++ 单例
        ok &= native_ptr_to_seval<cocos2d::myjsb::Goddess>((cocos2d::myjsb::Goddess*)result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_getInstance : Error processing arguments");
        return true;
    }

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_FUNC(js_cocos2d_myjsb_Goddess_getInstance);

static bool js_cocos2d_myjsb_Goddess_get_nickname(se::State& s) {
    cocos2d::myjsb::Goddess* cobj = (cocos2d::myjsb::Goddess*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_get_nickname : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 0) {
        std::string result = cobj->getNickName();
        ok &= std_string_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_get_nickname : Error processing arguments");
        return true;
    }
    
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_PROP_GET(js_cocos2d_myjsb_Goddess_get_nickname);

static bool js_cocos2d_myjsb_Goddess_set_nickname(se::State& s) {
    cocos2d::myjsb::Goddess* cobj = (cocos2d::myjsb::Goddess*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_set_nickname : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 1) {
        std::string nickname;
        ok &= seval_to_std_string(args[0], &nickname);
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_set_nickname : Error processing new value");
        cobj->setNickName(nickname);

        return true;
    }

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
    return true;
}
SE_BIND_PROP_SET(js_cocos2d_myjsb_Goddess_set_nickname);

static bool js_cocos2d_myjsb_Goddess_get_age(se::State& s) {
    cocos2d::myjsb::Goddess* cobj = (cocos2d::myjsb::Goddess*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_get_age : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 0) {
        unsigned int result = cobj->getAge();
        ok &= uint32_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_get_age : Error processing arguments");
        return true;
    }
    
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_PROP_GET(js_cocos2d_myjsb_Goddess_get_age);

static bool js_cocos2d_myjsb_Goddess_set_age(se::State& s) {
    cocos2d::myjsb::Goddess* cobj = (cocos2d::myjsb::Goddess*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_set_age : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 1) {
        unsigned int age = 0;
        ok &= seval_to_uint32(args[0], (uint32_t*)&age);
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_set_age : Error processing new value");
        cobj->setAge(age);

        return true;
    }

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
    return true;
}
SE_BIND_PROP_SET(js_cocos2d_myjsb_Goddess_set_age);

static bool js_cocos2d_myjsb_Goddess_sayHi(se::State& s) {
    cocos2d::myjsb::Goddess* cobj = (cocos2d::myjsb::Goddess*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_sayHi : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 0) {
        std::string result = cobj->sayHi();
        ok &= std_string_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_sayHi : Error processing arguments");
        return true;
    }
    
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_FUNC(js_cocos2d_myjsb_Goddess_sayHi);

static bool js_cocos2d_myjsb_Goddess_receiveGift(se::State &s) {
    cocos2d::myjsb::Goddess *cobj = (cocos2d::myjsb::Goddess *) s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Goddess_receiveGift : Invalid Native Object");

    const auto &args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    do {
        if (argc == 2) {
            cocos2d::myjsb::Gift gift;
            ok &= seval_to_Gift(args[0], &gift);
            std::function<void(const std::string& msg, const int val)> callback;
            
            if (args[1].isObject() && args[1].toObject()->isFunction()) {
                se::Value jsThis(s.thisObject());
                se::Value jsFunc(args[1]);

                // 如果目标类是一个单例则不能用 se::Object::attachObject 去关联
                // 必须使用 se::Object::root，无需关心 unroot，unroot 的操作会随着 lambda 的销毁触发 jsFunc 的析构，在 se::Object 的析构函数中进行 unroot 操作。
                // 如果使用 s.thisObject->attachObject(jsFunc.toObject);会导致对应的 func 和 target 永远无法被释放，引发内存泄露。
                jsFunc.toObject()->root(); 
                auto lambda = [=](const std::string& msg,
                                  const int val) -> void {
                    se::ScriptEngine::getInstance()->clearException();
                    se::AutoHandleScope hs;
                    CC_UNUSED bool ok = true;
                    se::ValueArray args;

                    args.resize(2);
                    ok &= std_string_to_seval(msg, &args[0]);
                    ok &= int32_to_seval(val, &args[1]);

                    se::Value rval;
                    se::Object* thisObj = jsThis.isObject() ? jsThis.toObject() : nullptr;
                    se::Object* funcObj = jsFunc.toObject();
                    // 执行 JS 方法回调
                    bool succeed = funcObj->call(args, thisObj, &rval);
                    if (!succeed) {
                        se::ScriptEngine::getInstance()->clearException();
                    }
                };
                callback = lambda;
            } else {
                callback = nullptr;
            }

            SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Goddess_receiveGift: Error processing arguments");
            cobj->receiveGift(gift, callback);
            return true;
        }
    } while(false);

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2);
    return false;
}
SE_BIND_FUNC(js_cocos2d_myjsb_Goddess_receiveGift);

bool js_register_cocos2d_myjsb_Goddess(se::Object* obj) {
    auto cls = se::Class::create("Goddess", obj, nullptr, nullptr);

    cls->defineStaticFunction("getInstance", _SE(js_cocos2d_myjsb_Goddess_getInstance));
    cls->defineProperty("nickname", _SE(js_cocos2d_myjsb_Goddess_get_nickname), _SE(js_cocos2d_myjsb_Goddess_set_nickname));
    cls->defineProperty("age", _SE(js_cocos2d_myjsb_Goddess_get_age), _SE(js_cocos2d_myjsb_Goddess_set_age));
    cls->defineFunction("sayHi", _SE(js_cocos2d_myjsb_Goddess_sayHi));
    cls->defineFunction("receiveGift", _SE(js_cocos2d_myjsb_Goddess_receiveGift));

    cls->install();
    JSBClassType::registerClass<cocos2d::myjsb::Goddess>(cls);

    __jsb_cocos2d_myjsb_Goddess_proto = cls->getProto();
    __jsb_cocos2d_myjsb_Goddess_class = cls;

    se::ScriptEngine::getInstance()->clearException();
    return true;
}

se::Object* __jsb_cocos2d_myjsb_Gift_proto = nullptr;
se::Class* __jsb_cocos2d_myjsb_Gift_class = nullptr;

static bool js_cocos2d_myjsb_Gift_constructor(se::State& s) {
    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    do {
        if (argc == 0) {
            cocos2d::myjsb::Gift* cobj = new (std::nothrow) cocos2d::myjsb::Gift();
            s.thisObject()->setPrivateData(cobj);
            se::NonRefNativePtrCreatedByCtorMap::emplace(cobj);
            return true;
        } else if (argc == 1) {
            std::string name;
            ok &= seval_to_std_string(args[0], &name);
            SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_constructor : Error processing new value");

            cocos2d::myjsb::Gift* cobj = new (std::nothrow) cocos2d::myjsb::Gift(name);
            s.thisObject()->setPrivateData(cobj);
            se::NonRefNativePtrCreatedByCtorMap::emplace(cobj);
            return true;
        }
    } while(false);

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d or %d", (int)argc, 0, 1);
    return false;
}
SE_BIND_CTOR(js_cocos2d_myjsb_Gift_constructor, __jsb_cocos2d_myjsb_Gift_class, js_cocos2d_myjsb_Gift_finalize)

static bool js_cocos2d_myjsb_Gift_finalize(se::State& s) {
    CCLOGINFO("jsbindings: finalizing JS object %p (cocos2d::myjsb::Gift)", s.nativeThisObject());

    auto iter = se::NonRefNativePtrCreatedByCtorMap::find(s.nativeThisObject());
    if (iter != se::NonRefNativePtrCreatedByCtorMap::end())
    {
        se::NonRefNativePtrCreatedByCtorMap::erase(iter);
        cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
        delete cobj;
    }

    return true;
}
SE_BIND_FINALIZE_FUNC(js_cocos2d_myjsb_Gift_finalize)

static bool js_cocos2d_myjsb_Gift_getName(se::State& s) {
    cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Gift_getName : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 0) {
        std::string result = cobj->getName();
        ok &= std_string_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_getName : Error processing arguments");
        return true;
    }
    
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
    return false;
}
SE_BIND_PROP_GET(js_cocos2d_myjsb_Gift_getName)

static bool js_cocos2d_myjsb_Gift_setName(se::State& s) {
    cocos2d::myjsb::Gift* cobj = (cocos2d::myjsb::Gift*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_cocos2d_myjsb_Gift_setName : Invalid Native Object");

    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;

    if (argc == 1) {
        std::string name;
        ok &= seval_to_std_string(args[0], &name);
        SE_PRECONDITION2(ok, false, "js_cocos2d_myjsb_Gift_setName : Error processing new value");
        cobj->setName(name);

        return true;
    }

    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
    return true;
}
SE_BIND_PROP_SET(js_cocos2d_myjsb_Gift_setName)

bool js_register_cocos2d_myjsb_Gift(se::Object* obj)
{
    auto cls = se::Class::create("Gift", obj, nullptr, _SE(js_cocos2d_myjsb_Gift_constructor));

    cls->defineProperty("name", _SE(js_cocos2d_myjsb_Gift_getName), _SE(js_cocos2d_myjsb_Gift_setName));
    cls->defineFinalizeFunction(_SE(js_cocos2d_myjsb_Gift_finalize));

    cls->install();
    JSBClassType::registerClass<cocos2d::myjsb::Gift>(cls);

    __jsb_cocos2d_myjsb_Gift_proto = cls->getProto();
    __jsb_cocos2d_myjsb_Gift_class = cls;

    se::ScriptEngine::getInstance()->clearException();
    return true;
}

bool register_all_myjsb(se::Object* obj)
{
    // Get the ns
    se::Value nsVal;
    if (!obj->getProperty("jsb", &nsVal))
    {
        se::HandleObject jsobj(se::Object::createPlainObject());
        nsVal.setObject(jsobj);
        obj->setProperty("jsb", nsVal);
    }
    se::Object* ns = nsVal.toObject();

    js_register_cocos2d_myjsb_Goddess(ns);
    js_register_cocos2d_myjsb_Gift(ns);

    return true;
}

#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
